"""
文件上传控制器
目的：处理文件上传相关的HTTP请求，包括图片、文档等文件的上传
职责：验证上传请求、调用存储服务、返回上传结果
"""

import os
import imghdr
from flask import Blueprint, request, current_app
from werkzeug.utils import secure_filename
from werkzeug.exceptions import RequestEntityTooLarge

from app.utils.response import (
    Result, success_response, bad_request_response,
    internal_error_response, unauthorized_response
)
from app.utils.auth import token_required, get_current_user
from app.services.minio_service import MinioService
from app.config.database import SessionLocal
from app.models.image import Image

# 创建上传蓝图
upload_bp = Blueprint('upload', __name__, url_prefix='/api/upload')

# 初始化MinIO服务
minio_service = MinioService()

# 文件上传配置
MAX_CONTENT_LENGTH = 10 * 1024 * 1024  # 10MB
ALLOWED_IMAGE_EXTENSIONS = {'jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'}
ALLOWED_DOCUMENT_EXTENSIONS = {'pdf', 'doc', 'docx', 'xls', 'xlsx', 'txt', 'md'}


@upload_bp.errorhandler(RequestEntityTooLarge)
def handle_file_too_large(e):
    """处理文件过大错误"""
    return bad_request_response(f'上传文件大小不能超过{MAX_CONTENT_LENGTH / 1024 / 1024}MB')


def validate_file_type(file_data: bytes, filename: str) -> str:
    """
    验证文件类型

    Args:
        file_data: 文件二进制数据
        filename: 文件名

    Returns:
        str: 检测到的文件类型，失败返回空字符串
    """
    # 使用imghdr检测图片类型
    image_type = imghdr.what(None, file_data)
    if image_type:
        return f'image/{image_type}'

    # 根据扩展名判断
    ext = os.path.splitext(filename)[1].lower()
    if ext == '.pdf':
        return 'application/pdf'
    elif ext in ['.doc', '.docx']:
        return 'application/msword' if ext == '.doc' else 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    elif ext in ['.xls', '.xlsx']:
        return 'application/vnd.ms-excel' if ext == '.xls' else 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    elif ext == '.txt':
        return 'text/plain'
    elif ext == '.md':
        return 'text/markdown'

    return ''


def save_image_metadata(user_id: int, result_data: dict, usage_type: str = 'other') -> Image:
    """
    保存图片元数据到数据库

    Args:
        user_id: 用户ID
        result_data: MinIO上传结果数据
        usage_type: 用途类型字符串（avatar/article/checkin/comment/other）

    Returns:
        Image: 图片记录对象
    """
    # 将字符串类型转换为数字类型
    usage_type_map = {
        'other': 1,
        'avatar': 2,
        'article': 3,
        'checkin': 4,
        'comment': 5
    }
    usage_type_int = usage_type_map.get(usage_type, 1)

    db = SessionLocal()
    try:
        image = Image(
            filename=result_data.get('original_name', ''),
            file_path=result_data['object_name'],
            file_size=result_data['size'],
            file_type=result_data['content_type'],
            width=None,  # 可以后续添加图片尺寸检测
            height=None,
            user_id=user_id,
            usage_type=usage_type_int,
            status=1
        )
        db.add(image)
        db.commit()
        db.refresh(image)
        return image
    finally:
        db.close()


@upload_bp.route('/image', methods=['POST'])
@token_required
def upload_image():
    """
    上传图片文件

    支持单文件上传，文件大小限制10MB
    支持的格式：jpg, jpeg, png, gif, webp, bmp

    Returns:
        Result: 上传结果
    """
    try:
        # 获取当前用户
        current_user = get_current_user()
        if not current_user:
            return unauthorized_response('用户认证失败')

        user_id = current_user.id

        # 检查是否有文件上传
        if 'file' not in request.files:
            return bad_request_response('未选择上传文件')

        file = request.files['file']
        if file.filename == '':
            return bad_request_response('文件名为空')

        # 读取文件数据
        file_data = file.read()
        if len(file_data) == 0:
            return bad_request_response('文件内容为空')

        # 验证文件大小
        if len(file_data) > MAX_CONTENT_LENGTH:
            return bad_request_response(f'文件大小不能超过{MAX_CONTENT_LENGTH / 1024 / 1024}MB')

        # 验证文件类型
        content_type = validate_file_type(file_data, file.filename)
        if not content_type or not content_type.startswith('image/'):
            return bad_request_response('只允许上传图片文件（jpg, jpeg, png, gif, webp, bmp）')

        # 获取用途类型
        usage_type = request.form.get('usage_type', 'other')
        if usage_type not in ['avatar', 'article', 'checkin', 'comment', 'other']:
            usage_type = 'other'

        # 上传文件到MinIO
        result = minio_service.upload_image(file_data, content_type)

        if result.code != 200:
            return result.to_response()

        # 添加原始文件名到结果数据
        result.data['original_name'] = secure_filename(file.filename)

        # 保存图片元数据到数据库
        image = save_image_metadata(
            user_id=user_id,
            result_data=result.data,
            usage_type=usage_type
        )

        # 返回完整数据
        response_data = {
            'image': image.to_dict(),
            'url': result.data['url']
        }

        return Result.success(data=response_data, message='图片上传成功').to_response()

    except Exception as e:
        return internal_error_response(f'上传失败：{str(e)}')


@upload_bp.route('/avatar', methods=['POST'])
@token_required
def upload_avatar():
    """
    上传用户头像

    专用头像上传接口，文件大小限制2MB
    自动按用户ID组织存储路径

    Returns:
        Result: 上传结果
    """
    try:
        # 获取当前用户
        current_user = get_current_user()
        if not current_user:
            return unauthorized_response('用户认证失败')

        user_id = current_user.id

        # 检查是否有文件上传
        if 'file' not in request.files:
            return bad_request_response('未选择上传文件')

        file = request.files['file']
        if file.filename == '':
            return bad_request_response('文件名为空')

        # 读取文件数据
        file_data = file.read()
        if len(file_data) == 0:
            return bad_request_response('文件内容为空')

        # 验证文件类型
        content_type = validate_file_type(file_data, file.filename)
        if not content_type or not content_type.startswith('image/'):
            return bad_request_response('只允许上传图片文件（jpg, jpeg, png, gif）')

        # 上传头像到MinIO
        result = minio_service.upload_avatar(file_data, content_type, user_id)

        if result.code != 200:
            return result.to_response()

        # 添加原始文件名到结果数据
        result.data['original_name'] = secure_filename(file.filename)

        # 保存头像元数据到数据库
        image = save_image_metadata(
            user_id=user_id,
            result_data=result.data,
            usage_type='avatar'
        )

        # 更新用户表的avatar字段（只存储文件路径，不存储完整URL）
        db = SessionLocal()
        try:
            from app.models.user import User
            user = db.query(User).filter(User.id == user_id).first()
            if user:
                # 存储文件路径而不是完整URL，避免超出字段长度限制
                user.avatar = result.data['object_name']
                db.commit()
        finally:
            db.close()

        # 返回完整数据
        response_data = {
            'image': image.to_dict(),
            'avatar_url': result.data['avatar_url']
        }

        return Result.success(data=response_data, message='头像上传成功').to_response()

    except Exception as e:
        return internal_error_response(f'上传失败：{str(e)}')


@upload_bp.route('/checkin-image', methods=['POST'])
@token_required
def upload_checkin_image():
    """
    上传打卡图片

    专用打卡图片上传接口

    Returns:
        Result: 上传结果
    """
    try:
        # 获取当前用户
        current_user = get_current_user()
        if not current_user:
            return unauthorized_response('用户认证失败')

        user_id = current_user.id

        # 获取打卡记录ID
        checkin_id = request.form.get('checkin_id', type=int)
        if not checkin_id:
            return bad_request_response('缺少打卡记录ID')

        # 检查是否有文件上传
        if 'file' not in request.files:
            return bad_request_response('未选择上传文件')

        file = request.files['file']
        if file.filename == '':
            return bad_request_response('文件名为空')

        # 读取文件数据
        file_data = file.read()
        if len(file_data) == 0:
            return bad_request_response('文件内容为空')

        # 验证文件大小
        if len(file_data) > MAX_CONTENT_LENGTH:
            return bad_request_response(f'文件大小不能超过{MAX_CONTENT_LENGTH / 1024 / 1024}MB')

        # 验证文件类型
        content_type = validate_file_type(file_data, file.filename)
        if not content_type or not content_type.startswith('image/'):
            return bad_request_response('只允许上传图片文件')

        # 上传打卡图片到MinIO
        result = minio_service.upload_checkin_image(file_data, content_type, user_id, checkin_id)

        if result.code != 200:
            return result.to_response()

        # 添加原始文件名到结果数据
        result.data['original_name'] = secure_filename(file.filename)

        # 保存图片元数据到数据库
        image = save_image_metadata(
            user_id=user_id,
            result_data=result.data,
            usage_type='checkin'
        )

        # 返回完整数据
        response_data = {
            'image': image.to_dict(),
            'image_url': result.data['image_url']
        }

        return Result.success(data=response_data, message='打卡图片上传成功').to_response()

    except Exception as e:
        return internal_error_response(f'上传失败：{str(e)}')


@upload_bp.route('/images', methods=['GET'])
@token_required
def get_user_images():
    """
    获取用户上传的图片列表

    支持分页和用途筛选

    Returns:
        Result: 图片列表
    """
    try:
        # 获取当前用户
        current_user = get_current_user()
        if not current_user:
            return unauthorized_response('用户认证失败')

        user_id = current_user.id

        # 获取查询参数
        page = request.args.get('page', 1, type=int)
        page_size = request.args.get('page_size', 20, type=int)
        usage_type = request.args.get('usage_type')

        # 验证分页参数
        if page < 1:
            page = 1
        if page_size < 1 or page_size > 100:
            page_size = 20

        # 查询数据库
        db = SessionLocal()
        try:
            query = db.query(Image).filter(
                Image.user_id == user_id,
                Image.status == 1
            )

            # 按用途筛选
            if usage_type:
                query = query.filter(Image.usage_type == usage_type)

            # 统计总数
            total = query.count()

            # 分页查询
            images = query.order_by(Image.created_at.desc()).offset(
                (page - 1) * page_size
            ).limit(page_size).all()

            # 构建响应数据
            items = [image.to_dict() for image in images]

            data = {
                'items': items,
                'total': total,
                'page': page,
                'page_size': page_size,
                'pages': (total + page_size - 1) // page_size
            }

            return Result.success(data=data, message='获取图片列表成功').to_response()

        finally:
            db.close()

    except Exception as e:
        return internal_error_response(f'获取失败：{str(e)}')


@upload_bp.route('/images/<int:image_id>', methods=['DELETE'])
@token_required
def delete_image(image_id: int):
    """
    删除图片

    软删除图片记录，并从MinIO删除文件

    Args:
        image_id: 图片ID

    Returns:
        Result: 删除结果
    """
    try:
        # 获取当前用户
        current_user = get_current_user()
        if not current_user:
            return unauthorized_response('用户认证失败')

        user_id = current_user.id

        # 查询图片记录
        db = SessionLocal()
        try:
            image = db.query(Image).filter(
                Image.id == image_id,
                Image.user_id == user_id,
                Image.status == 1
            ).first()

            if not image:
                return bad_request_response('图片不存在或无权限删除')

            # 从MinIO删除文件
            result = minio_service.delete_object(image.file_path, 'images')
            if result.code != 200:
                return result.to_response()

            # 软删除记录
            image.status = 0
            db.commit()

            return Result.success(message='图片删除成功').to_response()

        finally:
            db.close()

    except Exception as e:
        return internal_error_response(f'删除失败：{str(e)}')


@upload_bp.route('/images/<int:image_id>', methods=['GET'])
@token_required
def get_image_info(image_id: int):
    """
    获取图片详细信息

    Args:
        image_id: 图片ID

    Returns:
        Result: 图片信息
    """
    try:
        # 获取当前用户
        current_user = get_current_user()
        if not current_user:
            return unauthorized_response('用户认证失败')

        user_id = current_user.id

        # 查询图片记录
        db = SessionLocal()
        try:
            image = db.query(Image).filter(
                Image.id == image_id,
                Image.user_id == user_id,
                Image.status == 1
            ).first()

            if not image:
                return bad_request_response('图片不存在')

            return Result.success(data=image.to_dict(), message='获取图片信息成功').to_response()

        finally:
            db.close()

    except Exception as e:
        return internal_error_response(f'获取失败：{str(e)}')